package com.xplay.xpocker.util;

import com.xplay.xpocker.meta.realize.MahjongUserChannel;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author wanjie
 * @date 2021/3/10 20:31
 */

public class CardUtil {

    private static final Integer CARD_TYPE_WAN = 0;
    private static final Integer CARD_TYPE_TIAO = 1;
    private static final Integer CARD_TYPE_TONG = 2;

    public static final String ACTION_COVER = "被";

    public static final String ACTION_BY_OWN = "自摸";

    public static final String ACTION_SHOOTING = "放炮";

    public static final String ACTION_CALLING = "查叫";

    public static final String ACTION_CLAIM = "赔叫";

    public static final String ACTION_BAR = "杠";

    public static final String ACTION_MING_BAR = "明杠";

    public static final String ACTION_AN_BAR = "暗杠";

    public static final String ACTION_DIAN_BAR = "点杠";

    public static final String ACTION_RECEIVE = "接炮";

    public static final String PING_HU = "平胡";
    public static final String DA_DUI = "大队子";
    public static final String JIN_GOU = "金钩钩";
    public static final String QI_DUI = "七对";
    public static final String QING_YI_SHE = "清一色";
    public static final String QING_DA_DUI = "清大对";
    public static final String QING_JIN_GOU = "清金钩";
    public static final String QING_QI_DUI = "清七对";


    /**
     * 倍数
     */
    public static final Integer ZERO = 0;
    public static final Integer ONE = 1;
    public static final Integer DOUBLE = 2;
    public static final Integer TRIPLE = 3;
    public static final Integer FOURFOLD = 4;
    public static final Integer QUINTUPLE = 5;
    public static final Integer SIXFOLD = 6;
    public static final Integer SEVENFOLD = 7;
    public static final Integer EIGHTFOLD = 8;
    public static final Integer NINEFOLD = 9;


    /**
     * 判断牌型
     * 1. 小胡
     * 2. 大对子
     * 3. 清一色
     * 4. 七对
     * 5. 清大对
     * 6. 金钩
     * 7.
     * @param args
     */

    /**
     * 分为两组 一组手牌   一组装了的牌
     * touchBar 里面会存在 4张牌  所以 touch bar 要做移除元素
     * 注意 调用此方法的时候会做元素移除 请
     *
     * @param userCard
     * @param touchBar
     */
    public static WzHuCardType checkCardType(List<Integer> userCard, List<Integer> touchBar) {
        ArrayList<Integer> userCardTemp = new ArrayList<>(userCard);
        WzHuCardType cardType = null;
        boolean threeSteps = false;
        if (!checkUserHu(userCardTemp)) {
            return null;
        }
        if (null != touchBar) {
            ArrayList<Integer> touchBarTemp = new ArrayList<>(touchBar);
            // 碰的牌中有可能会出现 4张 只保留3张
            removeCardNumber(touchBarTemp, 3);
            userCardTemp.addAll(touchBarTemp);
        }
        Map<Integer, LinkedList<Integer>> linkedListMap = cardToGroup(userCardTemp);
        userCardTemp.sort(Comparator.naturalOrder());
        if (linkedListMap.size() == 1) {
            if (null != touchBar && touchBar.size() >= 9) {
                threeSteps = true;
            }
            if (checkPokerAA(new ArrayList<Integer>(userCardTemp)) && userCardTemp.size() == 14) {
                Integer loong = checkCardNumber(userCardTemp, FOURFOLD).size();
                cardType = new WzHuCardType(QING_QI_DUI + (loong > 0 ? String.format("、%s龙", loong) : ""), DOUBLE + loong, threeSteps);
            } else if (checkPokerAaaa(new ArrayList<>(userCardTemp)) && userCard.size() == 2) {
                cardType = new WzHuCardType(QING_JIN_GOU, TRIPLE, threeSteps);
            } else if (checkPokerAaaa(new ArrayList<>(userCardTemp))) {
                cardType = new WzHuCardType(QING_DA_DUI, DOUBLE, threeSteps);
            } else {
                cardType = new WzHuCardType(QING_YI_SHE, ONE, threeSteps);
            }
        } else {
            // 在两种牌型 及以上
            if (checkPokerAA(new ArrayList<Integer>(userCardTemp)) && userCardTemp.size() == 14) {
                Integer loong = checkCardNumber(userCardTemp, 4).size();
                cardType = new WzHuCardType(loong.toString() + (loong > 0 ? String.format("、%s龙", loong) : ""), ONE + loong);
            } else if (checkPokerAaaa(new ArrayList<>(userCardTemp)) && userCard.size() == 2) {
                cardType = new WzHuCardType(JIN_GOU, DOUBLE);
            } else if (checkPokerAaaa(new ArrayList<>(userCardTemp))) {
                cardType = new WzHuCardType(DA_DUI, ONE);
            } else {
                cardType = new WzHuCardType(PING_HU, ZERO);
            }
        }
        return cardType;
    }


    /**
     * 按断重复的牌 有几个
     * 比如  1,1,1,2,2,2    3
     * 这种情况下返回  2
     *
     * @param userCard
     * @param sum
     * @return
     */
    public static ArrayList<Integer> checkCardNumber(List<Integer> userCard, Integer sum) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        userCard.sort(Comparator.naturalOrder());
        Integer temp = null;
        int tally = 0;
        for (Integer card : userCard) {
            if (temp == null) {
                temp = card;
                tally++;
                continue;
            }
            if (tally >= sum && card == temp) {
                continue;
            } else if (card == temp) {
                tally++;
            } else {
                temp = card;
                tally = 1;
            }
            if (tally >= sum) {
                if (result.indexOf(temp) == -1) {
                    result.add(temp);
                }
            }

        }
        return result;
    }

    /**
     * 杠牌没有办法 计算胡牌 所以要移除为3张
     *
     * @param userCard
     * @param sum
     */

    public static List<Integer> removeCardNumber(List<Integer> userCard, Integer sum) {
        userCard.sort(Comparator.naturalOrder());
        Integer temp = null;
        int tally = 0;
        Iterator<Integer> iterator = userCard.iterator();
        while (iterator.hasNext()) {
            Integer card = iterator.next();
            if (temp == null) {
                temp = card;
                tally++;
                continue;
            }
            if (tally >= sum && card == temp) {
                iterator.remove();
            } else if (card == temp) {
                tally++;
            } else {
                temp = card;
                tally = 1;
            }
        }
        return userCard;
    }


    public static void main(String[] args) {
        System.out.println(CardUtil.checkUserHuCard(Arrays.asList(new Integer[]{5})));
        System.out.println(new WzHuCardType(PING_HU, ZERO).getFraction());

        // 玩家手牌
        List<Integer> userCard = Arrays.asList(new Integer[]{6, 7, 7, 7, 7});
        /* System.out.println(removeCardNumber(new ArrayList<>(userCard), 3));
        System.out.println(checkCardNumber(userCard, 3));*/
        // 玩家撞的牌
        List<Integer> touchBar = Arrays.asList(new Integer[]{20, 20, 20, 20});
        System.out.println(checkUserCallCard(userCard));
        System.out.println(checkCardType(new ArrayList<>(userCard), touchBar));
        //checkCardType(new ArrayList<>(userCard), new ArrayList<>(touchBar));

    }


    public static ArrayList<Integer> checkUserBar(ArrayList<Integer> userCards, ArrayList<Integer> userAllTouchBarCards) {
        ArrayList<Integer> result = null;

        ArrayList<Integer> barList = CardUtil.checkCardNumber(userCards, 4);
        if (barList.size() >= 1) {
            result = barList;
        }
        if (null != userAllTouchBarCards && userAllTouchBarCards.size() > 0) {
            for (Integer cardTemp : userCards) {
                if (Collections.frequency(userAllTouchBarCards, cardTemp) == 3) {
                    if (result == null) {
                        result = new ArrayList<>();
                    }
                    result.add(cardTemp);
                }
            }
        }
        return result;
    }

    /**
     * 判断用户 是否可以报叫 并且返回 保叫过后出只能出那些牌
     *
     * @param userCards
     * @return
     */


    public static ArrayList<Integer> checkUserCallCard(List<Integer> userCards) {
        ArrayList<Integer> needExportCardResult = new ArrayList<>();
        for (Integer i = 0, j = userCards.size(); i < j; i++) {
            List<Integer> tempCards = new ArrayList<>(userCards);
            Integer cardInfo = tempCards.get(i);
            removeCard(tempCards, cardInfo);
            List<Integer> huCards = checkUserHuCard(tempCards);
            if (null != huCards && huCards.size() > 0 && !needExportCardResult.contains(cardInfo)) {
                needExportCardResult.add(cardInfo);
            }
        }
        return needExportCardResult;
    }

    /**
     * 判断用户是否胡牌  并且返回胡那张牌
     *
     * @param userCards
     * @return
     */

    public static ArrayList<Integer> checkUserHuCard(List<Integer> userCards) {
        ArrayList<Integer> huCard = new ArrayList<>();
        for (int cardId = 1; cardId <= 27; cardId++) {
            ArrayList<Integer> userCard = new ArrayList<>(userCards);
            userCard.add(cardId);
            if (checkUserHu(userCard)) {
                huCard.add(cardId);
            }
        }
        return huCard;
    }

    /**
     * 从用户的手牌中移除指定的牌
     */
    public static void removeCard(List<Integer> userCards, Integer exportCard) {
        int userCardSize = userCards.size();
        for (int i = userCardSize - 1; i >= 0; i--) {
            if (userCards.get(i) == exportCard) {
                userCards.remove(i);
                return;
            }
        }
    }

    /**
     * 判断用户手牌是否可以碰
     *
     * @param userCards
     * @return
     */
    public static boolean checkCardTouch(List<Integer> userCards, Integer exportCard) {
        return Collections.frequency(userCards, exportCard) >= 2;
    }

    /**
     * 判断用户手牌是否可以碰
     *
     * @param userCards
     * @return
     */
    public static boolean checkCardBar(List<Integer> userCards, Integer exportCard) {
        return Collections.frequency(userCards, exportCard) == 3;
    }


    public static void cardByGroup(Map<Integer, LinkedList<Integer>> cardGroup, Integer val, Integer type) {
        if (cardGroup.get(type) == null) {
            LinkedList<Integer> card = new LinkedList<>();
            card.add(val);
            cardGroup.put(type, card);
        } else {
            cardGroup.get(type).add(val);
        }
    }


    public static void removeCard(List<Integer> userCards, List<Integer> outCards) {
        for (Integer inCard : outCards) {
            int userCardSize = userCards.size();
            for (int i = userCardSize - 1; i >= 0; i--) {
                if (userCards.get(i) == inCard) {
                    userCards.remove(i);
                    break;
                }
            }
        }

    }

    public static Map<Integer, LinkedList<Integer>> cardToGroup(List<Integer> linklist) {
        Map<Integer, LinkedList<Integer>> cardGroup = new HashMap<Integer, LinkedList<Integer>>(3);
        //  3 *  9  =   27  张牌    1-9   10-18  29-27
        linklist.forEach(val -> {
            if (val >= 1 && val <= 9) {
                cardByGroup(cardGroup, val, CARD_TYPE_WAN);
            } else if (val >= 10 && val <= 18) {
                cardByGroup(cardGroup, val, CARD_TYPE_TIAO);
            } else {
                cardByGroup(cardGroup, val, CARD_TYPE_TONG);
            }
        });
        return cardGroup;
    }

    public static boolean checkUserHu(List<Integer> integers) {
        Integer length = integers.size();
        if (length != 2 && length != 5 && length != 8 && length != 11 && length != 14) {
            return false;
        }
        LinkedList<Integer> linklist = new LinkedList();
        integers.forEach((val) -> {
            linklist.add(val);
        });
        Map<Integer, LinkedList<Integer>> cardGroup = cardToGroup(linklist);
        if (cardGroup.get(CARD_TYPE_WAN) == null) {
            cardGroup.put(CARD_TYPE_WAN, new LinkedList<Integer>());
        }
        if (cardGroup.get(CARD_TYPE_TIAO) == null) {
            cardGroup.put(CARD_TYPE_TIAO, new LinkedList<Integer>());
        }
        if (cardGroup.get(CARD_TYPE_TONG) == null) {
            cardGroup.put(CARD_TYPE_TONG, new LinkedList<Integer>());
        }
        boolean result = false;
        //  0   1   2
        for (int index = 0; index < 3; index++) {
            // 这里需要实现深度拷贝 因为下面会对集合进行 元素移除
            HashMap<Integer, LinkedList<Integer>> tempCardGroup = new HashMap<Integer, LinkedList<Integer>>(3);
            tempCardGroup.put(CARD_TYPE_WAN, new LinkedList<Integer>(cardGroup.get(CARD_TYPE_WAN)));
            tempCardGroup.put(CARD_TYPE_TIAO, new LinkedList<Integer>(cardGroup.get(CARD_TYPE_TIAO)));
            tempCardGroup.put(CARD_TYPE_TONG, new LinkedList<Integer>(cardGroup.get(CARD_TYPE_TONG)));
            if (result) {
                break;
            }
            //  分组
            int a = index + 1;
            int b = index + 2;
            int size = tempCardGroup.size();
            if (a > size - 1) {
                a = a % 3;
            }
            if (b > size - 1) {
                b = b % 3;
            }
            boolean aHu = true, bHu = true, cHu = true;
            if (tempCardGroup.get(index).size() % 3 == 2
                    && tempCardGroup.get(a).size() % 3 == 0
                    && tempCardGroup.get(b).size() % 3 == 0) {
                // 第一种情况 判断  万条筒  3个牌型 是不是 只剩下2张
                if (tempCardGroup.get(index).size() > 0) {
                    tempCardGroup.get(index).sort(Comparator.naturalOrder());
                    aHu = checkPokerAabcd(tempCardGroup.get(index));
                }
                if (tempCardGroup.get(a).size() > 0) {
                    tempCardGroup.get(a).sort(Comparator.naturalOrder());
                    bHu = checkPokerAabc(tempCardGroup.get(a));
                }
                if (tempCardGroup.get(b).size() > 0) {
                    tempCardGroup.get(b).sort(Comparator.naturalOrder());
                    cHu = checkPokerAabc(tempCardGroup.get(b));
                }
                result = (aHu && bHu && cHu);
            } else if (linklist.size() == 14 && tempCardGroup.get(index).size() % 2 == 0
                    && tempCardGroup.get(a).size() % 2 == 0
                    && tempCardGroup.get(b).size() % 2 == 0) {
                if (tempCardGroup.get(index).size() > 0) {
                    tempCardGroup.get(index).sort(Comparator.naturalOrder());
                    aHu = checkPokerAA(tempCardGroup.get(index));
                }
                if (tempCardGroup.get(a).size() > 0) {
                    tempCardGroup.get(a).sort(Comparator.naturalOrder());
                    bHu = checkPokerAA(tempCardGroup.get(a));
                }
                if (tempCardGroup.get(b).size() > 0) {
                    tempCardGroup.get(b).sort(Comparator.naturalOrder());
                    cHu = checkPokerAA(tempCardGroup.get(b));
                }
                // 第二种情况  判断  手牌 是不是 全都是对子
                result = (aHu && bHu && cHu);
            }

        }
        return result;
    }

    /**
     * 余2 检测龙 7对
     *
     * @param list
     * @return
     */

    private static boolean checkPokerAA(List<Integer> list) {
        if (list.size() == 0) {
            return true;
        }
        if (list.size() == 2) {
            if (list.get(0) == list.get(1)) {
                return true;
            } else {
                return false;
            }
        }
        boolean flag;
        for (int start = list.size() - 1; start > 1; start--) {
            Integer pokerOne = list.get(start);
            Integer pokerTwo = list.get(start - 1);
            // 找刻子
            if (pokerOne == pokerTwo) {
                list.remove(start);
                list.remove(start - 1);
                flag = checkPokerAA(list);
                if (flag) {
                    return true;
                } else {
                    list.add(start - 2, pokerOne);
                    list.add(start - 2, pokerOne);
                    break;
                }
            }
        }
        return false;
    }

    /**
     * 余2 带顺子或者刻字
     *
     * @param list
     * @return
     */

    public static boolean checkPokerAabcd(LinkedList<Integer> list) {
        if (list.size() == 0) {
            return true;
        }
        if (list.size() == 2) {
            if (list.get(0) == list.get(1)) {
                return true;
            } else {
                return false;
            }
        }
        boolean flag;
        for (int start = list.size() - 1; start > 1; start--) {
            Integer pokerOne = list.get(start);
            Integer pokerTwo = list.get(start - 1);
            Integer pokerThree = list.get(start - 2);
            // 找刻子
            if (pokerOne == pokerTwo && pokerTwo == pokerThree) {
                list.remove(start);
                list.remove(start - 1);
                list.remove(start - 2);
                flag = checkPokerAabcd(list);
                if (flag) {
                    return true;
                } else {
                    list.add(start - 2, pokerOne);
                    list.add(start - 2, pokerOne);
                    list.add(start - 2, pokerOne);
                    break;
                }
            }
        }
        int temp = list.size() - 1;
        // 找顺子的话 循环N-1次 [654321] 每后两个组合像前面找第一个不行就在往前面找一个
        for (int start = temp; start > 1; start--) {
            // 找顺子 123 判断相邻的两个值是否相等
            Integer pokerCard = list.get(start);
            Integer pokerCard2 = list.get(start - 1);
            if (pokerCard - 1 == pokerCard2) {
                // 找到第3个坐标
                int i = start - 2;
                while (i >= 0) {
                    Integer pokerCard3 = list.get(i);
                    // 判断第1,2是否相等
                    if (pokerCard2 - 1 == pokerCard3) {
                        list.remove(start);
                        list.remove(start - 1);
                        list.remove(i);
                        flag = checkPokerAabcd(list);
                        if (!flag) {
                            list.add(i, pokerCard3);
                            list.add(start - 1, pokerCard);
                            list.add(start - 1, pokerCard2);
                            start = temp--;
                            break;
                        } else {
                            return true;
                        }
                    }
                    i--;
                }
            }
        }
        return false;
    }

    /**
     * 整除
     *
     * @param list
     * @return
     */

    private static boolean checkPokerAabc(List<Integer> list) {
        if (list.size() == 0) {
            return true;
        }
        boolean flag;
        for (int start = list.size() - 1; start > 1; start--) {
            Integer pokerOne = list.get(start);
            Integer pokerTwo = list.get(start - 1);
            Integer pokerThree = list.get(start - 2);
            // 找刻子
            if (pokerOne == pokerTwo && pokerTwo == pokerThree) {
                list.remove(start);
                list.remove(start - 1);
                list.remove(start - 2);
                flag = checkPokerAabc(list);
                if (flag) {
                    return true;
                } else {
                    list.add(start - 2, pokerOne);
                    list.add(start - 2, pokerOne);
                    list.add(start - 2, pokerOne);
                    break;
                }
            }
        }
        int temp = list.size() - 1;
        // 找顺子的话 循环N-1次 [654321] 每后两个组合像前面找第一个不行就在往前面找一个
        for (int start = temp; start > 1; start--) {
            // 找顺子 123 判断相邻的两个值是否相等
            Integer pokerCard = list.get(start);
            Integer pokerCard2 = list.get(start - 1);
            if (pokerCard - 1 == pokerCard2) {
                // 找到第3个坐标
                int i = start - 2;
                while (i >= 0) {
                    Integer pokerCard3 = list.get(i);
                    // 判断第1,2是否相等
                    if (pokerCard2 - 1 == pokerCard3) {
                        list.remove(start);
                        list.remove(start - 1);
                        list.remove(i);
                        flag = checkPokerAabc(list);
                        if (!flag) {
                            list.add(i, pokerCard3);
                            list.add(start - 1, pokerCard);
                            list.add(start - 1, pokerCard2);
                            start = temp--;
                            break;
                        } else {
                            return true;
                        }
                    }
                    i--;
                }
            }
        }
        return false;
    }


    /**
     * 整除
     *
     * @param list
     * @return
     */

    private static boolean checkPokerAaaa(List<Integer> list) {
        if (list.size() == 0) {
            return true;
        }
        if (list.size() == 2) {
            if (list.get(0) == list.get(1)) {
                return true;
            } else {
                return false;
            }
        }
        boolean flag;
        for (int start = list.size() - 1; start > 1; start--) {
            Integer pokerOne = list.get(start);
            Integer pokerTwo = list.get(start - 1);
            Integer pokerThree = list.get(start - 2);
            // 找刻子
            if (pokerOne == pokerTwo && pokerTwo == pokerThree) {
                list.remove(start);
                list.remove(start - 1);
                list.remove(start - 2);
                flag = checkPokerAaaa(list);
                if (flag) {
                    return true;
                } else {
                    list.add(start - 2, pokerOne);
                    list.add(start - 2, pokerOne);
                    list.add(start - 2, pokerOne);
                    break;
                }
            }
        }
        return false;
    }
}
